//---------------------------------------------------------------------------
#include <vcl.h>
#include <EIC.h>
#include <dir.h>
#include <FileCtrl.hpp>
#include <io.h>
#pragma hdrstop
//---------------------------------------------------------------------------
#include "CCompress.h"
#pragma package(smart_init)
//---------------------------------------------------------------------------
#define NOM_TAR "Temp.tar"   // Nom du fichier temporaire
#define DLL_BZ2 "libbz2.dll" // Nom de la dll
//---------------------------------------------------------------------------
// Compte le nombre de fichier dans un repertoire et ses sous repertoires
//---------------------------------------------------------------------------
int __fastcall CompterFichiers( const AnsiString &asDirectory, bool bRecursif )
{
    TSearchRec sr;
    int nbFile = 0;
    try {
        if (FindFirst(asDirectory + "\\*.*", faAnyFile, sr ) == 0) {
            do {
                if ( (sr.Attr & faDirectory) == faDirectory ) {
                    if ( bRecursif && (sr.Name != "." && sr.Name != "..") ) {
                        nbFile += CompterFichiers(asDirectory + "\\" + sr.Name, true);
                    }
                } else {
                    nbFile++;
                }
            } while(FindNext(sr) == 0);
        }
        FindClose(sr);

    } catch(Exception &e) {
        ShowMessage( e.Message ); }
    return nbFile;
}
//---------------------------------------------------------------------------
// ValidCtrCheck est utilisé est utilisé pour être sûr que les composants créés n'ont
// aucune fonction virtuelle pure.
//---------------------------------------------------------------------------
static inline void ValidCtrCheck(CCompress *)
{
    new CCompress(NULL);
}
//---------------------------------------------------------------------------
__fastcall CCompress::CCompress(TComponent* Owner)
    : TComponent(Owner)
{
    if (m_asPathTemp.IsEmpty()) {
        char szTemp[MAX_PATH]; ZeroMemory( szTemp, sizeof( szTemp));
        GetTempPath( sizeof( szTemp), szTemp);
        m_asPathTemp = AnsiString(szTemp);
    }
    m_asPathTemp     = IncludeTrailingBackslash(m_asPathTemp);
    m_nPourcent = 0;
}
//---------------------------------------------------------------------------
__fastcall CCompress::~CCompress()
{
}
//---------------------------------------------------------------------------
void __fastcall CCompress::SetDirectory(AnsiString asDirectory)
{
    m_asDirectory = asDirectory;
    if (!m_asDirectory.IsEmpty())
        m_asDirectory = ::IncludeTrailingBackslash(m_asDirectory);
}
//---------------------------------------------------------------------------
void __fastcall CCompress::SetDirectoryOut(AnsiString asDirectoryOut)
{
    m_asDirectoryOut = asDirectoryOut;
    if (!m_asDirectoryOut.IsEmpty())
        m_asDirectoryOut = ::IncludeTrailingBackslash(m_asDirectoryOut);
}
//---------------------------------------------------------------------------
bool __fastcall CCompress::Compress()
{
    bool    bRetour = false;
    void*   b;
    FILE*   fd;
    FILE*   fs;
    fpos_t  filepos;
    int     nBuf, bzerror, nWritten, nSize;
    char    buf[5000];
    AnsiString asPathSource(m_asPathTemp + NOM_TAR);
    AnsiString   asPathDest(m_asDirectoryOut + m_asFileName + ".bz2");

    HINSTANCE hInst;
    if ((hInst = (HINSTANCE)LoadLibrary(DLL_BZ2)) == NULL) {
        EICErreur("Impossible de charger : " + AnsiString(DLL_BZ2));
        return false;
    }
    TFctWrite FctWrite = (TFctWrite)GetProcAddress(hInst, "BZ2_bzWrite");
    if( !FctWrite ) throw("Impossible de charger FctWrite");
    TFctWriteOpen FctWriteOpen = (TFctWriteOpen)GetProcAddress(hInst, "BZ2_bzWriteOpen");
    if( !FctWriteOpen ) throw("Impossible de charger FctWriteOpen");
    TFctWriteClose FctWriteClose = (TFctWriteClose)GetProcAddress(hInst, "BZ2_bzWriteClose");
    if( !FctWriteClose ) throw("Impossible de charger FctWriteClose");

    if( (fd = fopen(asPathDest.c_str()  , "wb")) == NULL ) goto ERREUR_IO;
    if( (fs = fopen(asPathSource.c_str(), "rb")) == NULL ) goto ERREUR_IO;
    nSize = filelength(_fileno(fs));

    b = FctWriteOpen ( &bzerror, fd, 9, 0 ,0 );
    if (bzerror != BZ_OK) goto ERREUR;

    while ( !feof(fs) ) {
        nWritten = fread(buf, sizeof(char),sizeof(buf),fs);
        if(ferror(fs)) goto ERREUR_IO;
        if (nWritten > 0) FctWrite ( &bzerror, b, buf, nWritten );
        if (bzerror == BZ_IO_ERROR) goto ERREUR;
        m_nPourcent = (fgetpos(fs, &filepos), (filepos*100)/nSize);
        if (FOnPourcent) {
            FOnPourcent(this, m_nPourcent, "Compréssion en cours...");
            Application->ProcessMessages();
        }
    }

ERREUR:
    FctWriteClose ( &bzerror, b,0 ,0 ,0 );
    switch (bzerror) {
        case BZ_CONFIG_ERROR:
            EICErreur("Problème de configuration, vérifier les options de compils.");break;
        case BZ_IO_ERROR:
            ERREUR_IO:
            EICErreur("Erreur d'écriture ou de lecture dans le fichier");
            break;
        case BZ_MEM_ERROR: OutOfMemoryError(); break;
        case BZ_OK:
            bRetour = true;
            break;
        default: EICErreur("Erreur inconnue !");
    }
    if (fs) fclose(fs);
    if (fd) fclose(fd);
    FreeLibrary(hInst);
    return bRetour;
}
//---------------------------------------------------------------------------
bool __fastcall CCompress::Decompress()
{
    bool bRetour = false;
    FILE*   fs = NULL;
    FILE*   fd = NULL;
    void*   b;
    int     nBuf, bzerror, nWritten;
    char    buf[5000];
    AnsiString asPathSource(m_asDirectory + m_asFileName + ".bz2");
    AnsiString   asPathDest(m_asPathTemp + NOM_TAR);
    int   nUnused = 0;
    char *unusedTmp;

    HINSTANCE hInst;
    if ((hInst = (HINSTANCE)LoadLibrary(DLL_BZ2)) == NULL) {
        EICErreur("Impossible de charger : " + AnsiString(DLL_BZ2));
        return false;
    }
    TFctRead FctRead = (TFctRead)GetProcAddress(hInst, "BZ2_bzRead");
    if( !FctRead ) throw("Impossible de charger FctRead");
    TFctReadOpen FctReadOpen = (TFctReadOpen)GetProcAddress(hInst, "BZ2_bzReadOpen");
    if( !FctReadOpen ) throw("Impossible de charger FctReadOpen");
    TFctReadClose FctReadClose = (TFctReadClose)GetProcAddress(hInst, "BZ2_bzReadClose");
    if( !FctReadClose ) throw("Impossible de charger FctReadClose");

    //TFctGetUnused FctGetUnused = (TFctGetUnused)GetProcAddress(hInst, "BZ2_bzReadGetUnused");
    //if( !FctGetUnused ) throw("Impossible de charger FctGetUnused");

    if( (fs = fopen( asPathSource.c_str(), "rb" )) == NULL ) goto ERREUR_IO;
    if( (fd = fopen( asPathDest.c_str()  , "wb" )) == NULL ) goto ERREUR_IO;

    b = FctReadOpen ( &bzerror, fs, 0, 0, NULL, 0);
    if (bzerror != BZ_OK) goto ERREUR;

    bzerror = BZ_OK;
    while (bzerror == BZ_OK) {
        ZeroMemory(buf,sizeof(buf));
        nBuf = FctRead( &bzerror, b, buf, sizeof(buf) );
        if ( (bzerror == BZ_OK || bzerror == BZ_STREAM_END) && nBuf > 0) {
            fwrite((void*)buf,sizeof(char),nBuf,fd);
            if (ferror(fd)) goto ERREUR_IO;
        }
    }
    if (bzerror != BZ_STREAM_END) goto ERREUR;
//    FctGetUnused ( &bzerror, b, (void**)(&unusedTmp), &nUnused );

ERREUR:
    FctReadClose ( &bzerror, b );
    switch (bzerror) {
        case BZ_CONFIG_ERROR:
            EICErreur("Problème de configuration, vérifier les options de compiles.");
            break;
        case BZ_IO_ERROR:
            ERREUR_IO:
            EICErreur("Erreur d'écriture ou de lecture dans le fichier");
            break;
        case BZ_DATA_ERROR:
            EICErreur("Problème de l'intégrité des données lors de la décompression.");
            break;
        case BZ_MEM_ERROR:
            OutOfMemoryError();
            break;
        case BZ_UNEXPECTED_EOF:
            EICErreur("La fin du fichier de compression est innatendu,\nle fichier est peut être corrompu ?");
            break;
        case BZ_OK:
            bRetour = true;
            break;
        case BZ_DATA_ERROR_MAGIC: break;
        default: EICErreur("Erreur inconnue !");
    }

    if (fs) fclose(fs);
    if (fd) fclose(fd);
    FreeLibrary(hInst);
    return bRetour;
}
//---------------------------------------------------------------------------
void __fastcall CCompress::CreerFichierTAR( const AnsiString &asDirectory )
{
    char szPath[MAX_PATH]; ZeroMemory( szPath, sizeof( szPath));
    TTarWriter *tarwriter = new TTarWriter( m_asPathTemp + NOM_TAR, fmCreate);
    //tarwriter->Magic = m_asMotDePasse;

    getcwd( szPath, MAX_PATH);
    chdir(asDirectory.c_str());
    AddTAR( tarwriter, EmptyStr);
    chdir( szPath);
    delete tarwriter;
}
//---------------------------------------------------------------------------
int __fastcall CCompress::AddTAR( TTarWriter *tarwriter, const AnsiString &asDir, int nNum )
{
    TSearchRec sr;
    try {

        if( FindFirst( asDir + "*.*", faAnyFile, sr)==0) {
            do {
                if(( sr.Name != ".") && ( sr.Name != "..")) {
                    if( sr.Attr & faDirectory)
                        nNum = AddTAR( tarwriter, IncludeTrailingBackslash( asDir + sr.Name), nNum+1 );
                    else {
                        tarwriter->AddFile( asDir + sr.Name, EmptyStr);
                        nNum ++;
                        m_nPourcent = (nNum * 100) / nbTotalFiles;
                        if (FOnPourcent) {
                            FOnPourcent(this, m_nPourcent,"Création du fichier TAR");
                            Application->ProcessMessages();
                        }
                    }
                }
            } while( FindNext( sr) == 0);
            FindClose( sr);
        }

    } catch( Exception &e ) {
		ShowMessage( e.Message );
	}
    return nNum;
}
//---------------------------------------------------------------------------
void __fastcall CCompress::Compresser( void )
{
    CSablier Sablier;
    try {      
        Sablier.Wait();
        m_nPourcent = 0;
        nbTotalFiles = CompterFichiers( m_asDirectory, true );
        CreerFichierTAR( m_asDirectory );
        m_nPourcent = 0;
        Compress();
        Sablier.Fin();

    } catch( Exception &e ) {
		ShowMessage( e.Message );
	}
}
//---------------------------------------------------------------------------
void __fastcall CCompress::Compresser( TStringList *lstRepertoire )
{
    CSablier Sablier;
    try {
        ASSERT(lstRepertoire);

        Sablier.Wait();
        for (int i=0; i<lstRepertoire->Count; i++) {
            AnsiString asDirectory(::IncludeTrailingBackslash(lstRepertoire->Strings[i]));
            CreerFichierTAR( asDirectory );
        }

        Compress();
        Sablier.Fin();

    } catch( Exception &e ) {
		ShowMessage( e.Message );
	}
}
//---------------------------------------------------------------------------
void __fastcall CCompress::Decompresser( void )
{
    CSablier Sablier;
    __int64 nCurrentPos, nSize = 0;
    AnsiString asPathComplet = EmptyStr;
    TTarArchive *tararchive = NULL;

    try {
        ASSERT(!m_asDirectoryOut.IsEmpty());

        Sablier.Wait();
        m_nPourcent = 0;

        Decompress();

        // Restauration du fichier tar
        TTarDirRec DirRec;
        tararchive = new TTarArchive( m_asPathTemp + NOM_TAR, fmOpenRead);
        tararchive->Reset();
        //AnsiString asChaine = DirRec.Magic;
        while( tararchive->FindNext( DirRec)) {
            AnsiString strFilename(m_asDirectoryOut + DirRec.Name);
            strFilename = StringReplace( strFilename, "/", "\\", TReplaceFlags() << rfReplaceAll);
            if( !DirectoryExists( ExtractFilePath( strFilename)))
                ForceDirectories( ExtractFilePath( strFilename));
            tararchive->ReadFile( strFilename);
            m_nPourcent = (tararchive->GetFilePos(nCurrentPos, nSize),(nCurrentPos * 100) / nSize);
            if (FOnPourcent) {
                FOnPourcent(this, m_nPourcent,"Décompression en cours...");
                Application->ProcessMessages();
            }
        }
        Sablier.Fin();

    } catch( Exception &e ) {
		ShowMessage( e.Message );
	}
    if (tararchive) { delete tararchive; tararchive = NULL; }
}
//---------------------------------------------------------------------------
namespace Ccompress
{
    void __fastcall PACKAGE Register()
    {
         TComponentClass classes[1] = {__classid(CCompress)};
         RegisterComponents("EIC Outils", classes, 0);
    }
}
//---------------------------------------------------------------------------
 